using System.Net;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Security.Claims;
using CoreManager.Models.DTOs;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;

namespace CoreManager.API.Middleware
{
    /// <summary>
    /// 全局异常处理中间件
    /// 统一捕获和处理应用程序中的所有异常
    /// </summary>
    public class GlobalExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<GlobalExceptionHandlerMiddleware> _logger;
        private readonly IHostEnvironment _environment;

        public GlobalExceptionHandlerMiddleware(
            RequestDelegate next,
            ILogger<GlobalExceptionHandlerMiddleware> logger,
            IHostEnvironment environment)
        {
            _next = next;
            _logger = logger;
            _environment = environment;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                // 保存原始响应流
                var originalBodyStream = context.Response.Body;
                using (var responseBody = new MemoryStream())
                {
                    // 替换响应流，以便我们可以读取响应内容
                    context.Response.Body = responseBody;

                    await _next(context);

                    // 检查响应状态码，处理错误状态码（4xx, 5xx）
                    if (context.Response.StatusCode >= 400)
                    {
                        // 检查响应体是否为空或已写入内容
                        if (responseBody.Length == 0)
                        {
                            // 响应体为空，说明是中间件或框架直接返回的错误状态码
                            // 我们需要返回统一的错误格式
                            await HandleStatusCodeErrorAsync(context, originalBodyStream);
                            return;
                        }
                    }

                    // 将缓冲的响应写回原始流
                    responseBody.Seek(0, SeekOrigin.Begin);
                    await responseBody.CopyToAsync(originalBodyStream);
                }
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }

        /// <summary>
        /// 处理HTTP状态码错误（400、401、403、404、405、500等）
        /// </summary>
        private async Task HandleStatusCodeErrorAsync(HttpContext context, Stream originalBodyStream)
        {
            // 获取追踪ID
            var traceId = context.Items["RequestId"]?.ToString() ?? Guid.NewGuid().ToString("N")[..16];
            
            var statusCode = context.Response.StatusCode;
            var message = statusCode switch
            {
                400 => "请求参数无效",
                401 => "未授权访问，请先登录",
                403 => "您没有权限访问此资源",
                404 => "请求的资源不存在",
                405 => "不支持的请求方法",
                408 => "请求超时",
                415 => "不支持的媒体类型",
                429 => "请求过于频繁，请稍后重试",
                500 => "服务器内部错误",
                502 => "网关错误",
                503 => "服务暂时不可用",
                504 => "网关超时",
                _ => "请求处理失败"
            };

            var response = new ApiResponse<object>
            {
                Code = statusCode,
                Message = message,
                TraceId = traceId,
                Data = null
            };

            // 记录日志
            var request = context.Request;
            var path = $"{request.Method} {request.Path}{request.QueryString}";
            var userId = GetCurrentUserId(context);
            var userInfo = string.IsNullOrEmpty(userId) ? "匿名用户" : $"用户ID:{userId}";
            var clientIp = GetClientIpAddress(context);

            // 根据状态码选择日志级别和消息
            var (logLevel, logMessage) = statusCode switch
            {
                401 or 403 => (LogLevel.Warning, "⚠️ [请求ID:{TraceId}] 认证授权失败 → {Path} | {UserInfo} | 客户端IP:{ClientIp} | 状态码:{StatusCode}"),
                404 => (LogLevel.Information, "ℹ️ [请求ID:{TraceId}] 资源未找到 → {Path} | {UserInfo} | 客户端IP:{ClientIp} | 状态码:{StatusCode}"),
                >= 500 => (LogLevel.Error, "💥 [请求ID:{TraceId}] 服务器错误 → {Path} | {UserInfo} | 客户端IP:{ClientIp} | 状态码:{StatusCode}"),
                _ => (LogLevel.Warning, "⚠️ [请求ID:{TraceId}] 请求错误 → {Path} | {UserInfo} | 客户端IP:{ClientIp} | 状态码:{StatusCode}")
            };

            _logger.Log(logLevel, logMessage, traceId, path, userInfo, clientIp, statusCode);

            // 设置响应
            context.Response.ContentType = "application/json; charset=utf-8";
            context.Response.Headers["X-Error-TraceId"] = traceId;
            
            // 清空原有的响应体
            context.Response.Body = originalBodyStream;

            var jsonOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                WriteIndented = _environment.IsDevelopment(),
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            };

            var jsonResponse = JsonSerializer.Serialize(response, jsonOptions);
            await context.Response.WriteAsync(jsonResponse);
        }

        /// <summary>
        /// 处理异常并返回统一的错误响应
        /// </summary>
        private async Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            // 获取追踪ID
            var traceId = context.Items["RequestId"]?.ToString() ?? Guid.NewGuid().ToString("N")[..16];
            
            // 获取用户信息（用于日志）
            var userId = GetCurrentUserId(context);
            var userInfo = string.IsNullOrEmpty(userId) ? "匿名用户" : $"用户ID:{userId}";
            // 确定HTTP状态码和错误信息
            var (statusCode, message, errorCode) = GetErrorDetails(exception);
            // 记录异常日志
            LogException(exception, context, traceId, userInfo, statusCode);
            // 构建错误响应
            var response = BuildErrorResponse(exception, message, errorCode, traceId);
            // 设置响应内容
            context.Response.ContentType = "application/json; charset=utf-8";
            context.Response.StatusCode = (int)statusCode;
            // 添加自定义响应头
            context.Response.Headers["X-Error-TraceId"] = traceId;
            // 序列化并返回响应
            var jsonOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                WriteIndented = _environment.IsDevelopment(),
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            };
            var jsonResponse = JsonSerializer.Serialize(response, jsonOptions);
            await context.Response.WriteAsync(jsonResponse);
        }

        /// <summary>
        /// 根据异常类型确定HTTP状态码和错误信息
        /// </summary>
        private (HttpStatusCode statusCode, string message, int errorCode) GetErrorDetails(Exception exception)
        {
            return exception switch
            {
                // 业务异常
                BusinessException businessEx => (
                    HttpStatusCode.BadRequest,
                    businessEx.Message,
                    businessEx.ErrorCode
                ),

                // 验证异常
                ValidationException validationEx => (
                    HttpStatusCode.BadRequest,
                    validationEx.Message,
                    validationEx.ErrorCode
                ),

                // 未找到异常
                NotFoundException notFoundEx => (
                    HttpStatusCode.NotFound,
                    notFoundEx.Message,
                    notFoundEx.ErrorCode
                ),

                // 未授权异常
                UnauthorizedAccessException => (
                    HttpStatusCode.Unauthorized,
                    "未授权访问，请先登录",
                    401
                ),

                // 禁止访问异常
                ForbiddenException forbiddenEx => (
                    HttpStatusCode.Forbidden,
                    forbiddenEx.Message,
                    forbiddenEx.ErrorCode
                ),

                // 参数异常
                ArgumentNullException argNullEx => (
                    HttpStatusCode.BadRequest,
                    $"参数 '{argNullEx.ParamName}' 不能为空",
                    400
                ),

                ArgumentException argEx => (
                    HttpStatusCode.BadRequest,
                    argEx.Message,
                    400
                ),

                // 无效操作异常
                InvalidOperationException invalidOpEx => (
                    HttpStatusCode.BadRequest,
                    invalidOpEx.Message,
                    400
                ),

                // 超时异常
                TimeoutException => (
                    HttpStatusCode.RequestTimeout,
                    "请求超时，请稍后重试",
                    408
                ),

                // 其他未知异常
                _ => (
                    HttpStatusCode.InternalServerError,
                    _environment.IsDevelopment() ? exception.Message : "服务器内部错误，请稍后重试",
                    500
                )
            };
        }

        /// <summary>
        /// 构建错误响应对象
        /// </summary>
        private ApiResponse<object> BuildErrorResponse(Exception exception, string message, int errorCode, string traceId)
        {
            var response = new ApiResponse<object>
            {
                Code = errorCode,
                Message = message,
                TraceId = traceId,
                Data = null
            };

            // 开发环境下返回详细的异常信息
            if (_environment.IsDevelopment())
            {
                response.Data = new
                {
                    ExceptionType = exception.GetType().Name,
                    ExceptionMessage = exception.Message,
                    StackTrace = exception.StackTrace,
                    InnerException = exception.InnerException?.Message,
                    Source = exception.Source
                };
            }
            // 生产环境下，针对特定异常类型返回额外信息
            else if (exception is ValidationException validationEx && validationEx.Errors?.Any() == true)
            {
                response.Data = new
                {
                    Errors = validationEx.Errors
                };
            }

            return response;
        }

        /// <summary>
        /// 记录异常日志
        /// </summary>
        private void LogException(Exception exception, HttpContext context, string traceId, string userInfo, HttpStatusCode statusCode)
        {
            var request = context.Request;
            var path = $"{request.Method} {request.Path}{request.QueryString}";
            var clientIp = GetClientIpAddress(context);
            // 根据异常类型选择不同的日志级别
            var logLevel = statusCode switch
            {
                HttpStatusCode.BadRequest => LogLevel.Warning,
                HttpStatusCode.NotFound => LogLevel.Warning,
                HttpStatusCode.Unauthorized => LogLevel.Warning,
                HttpStatusCode.Forbidden => LogLevel.Warning,
                _ => LogLevel.Error
            };
            // 自定义异常（业务异常）使用警告级别，系统异常使用错误级别
            if (exception is BusinessException or ValidationException or NotFoundException or ForbiddenException)
            {
                _logger.Log(LogLevel.Warning,
                    "⚠️ [请求ID:{TraceId}] 业务异常 → {Path} | {UserInfo} | 客户端IP:{ClientIp} | 状态码:{StatusCode} | 异常类型:{ExceptionType} | 错误信息:{Message}",
                    traceId, path, userInfo, clientIp, (int)statusCode, exception.GetType().Name, exception.Message);
            }
            else
            {
                _logger.LogError(exception,
                    "💥 [请求ID:{TraceId}] 系统异常 → {Path} | {UserInfo} | 客户端IP:{ClientIp} | 状态码:{StatusCode} | 异常类型:{ExceptionType} | 错误信息:{Message}",
                    traceId, path, userInfo, clientIp, (int)statusCode, exception.GetType().Name, exception.Message);
            }
        }

        /// <summary>
        /// 获取当前用户ID
        /// </summary>
        private static string? GetCurrentUserId(HttpContext context)
        {
            return context.User?.FindFirst("sub")?.Value ??
                   context.User?.FindFirst("userId")?.Value ??
                   context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        }

        /// <summary>
        /// 获取客户端IP地址
        /// </summary>
        private static string GetClientIpAddress(HttpContext context)
        {
            var ipAddress = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ipAddress))
            {
                ipAddress = context.Request.Headers["X-Real-IP"].FirstOrDefault();
            }
            if (string.IsNullOrEmpty(ipAddress))
            {
                ipAddress = context.Connection.RemoteIpAddress?.ToString();
            }
            return ipAddress ?? "Unknown";
        }
    }

    #region 自定义异常类

    /// <summary>
    /// 业务异常基类
    /// </summary>
    public class BusinessException : Exception
    {
        public int ErrorCode { get; set; }

        public BusinessException(string message, int errorCode = 400) : base(message)
        {
            ErrorCode = errorCode;
        }

        public BusinessException(string message, Exception innerException, int errorCode = 400) 
            : base(message, innerException)
        {
            ErrorCode = errorCode;
        }
    }

    /// <summary>
    /// 验证异常
    /// </summary>
    public class ValidationException : Exception
    {
        public int ErrorCode { get; set; }
        public Dictionary<string, string[]>? Errors { get; set; }

        public ValidationException(string message, int errorCode = 400) : base(message)
        {
            ErrorCode = errorCode;
        }

        public ValidationException(string message, Dictionary<string, string[]> errors, int errorCode = 400) 
            : base(message)
        {
            ErrorCode = errorCode;
            Errors = errors;
        }
    }

    /// <summary>
    /// 未找到异常
    /// </summary>
    public class NotFoundException : Exception
    {
        public int ErrorCode { get; set; }

        public NotFoundException(string message, int errorCode = 404) : base(message)
        {
            ErrorCode = errorCode;
        }

        public NotFoundException(string resourceName, object key) 
            : base($"资源 '{resourceName}' (ID: {key}) 未找到")
        {
            ErrorCode = 404;
        }
    }

    /// <summary>
    /// 禁止访问异常
    /// </summary>
    public class ForbiddenException : Exception
    {
        public int ErrorCode { get; set; }

        public ForbiddenException(string message, int errorCode = 403) : base(message)
        {
            ErrorCode = errorCode;
        }

        public ForbiddenException() : base("您没有权限执行此操作")
        {
            ErrorCode = 403;
        }
    }

    #endregion

    /// <summary>
    /// 全局异常处理中间件扩展
    /// </summary>
    public static class GlobalExceptionHandlerMiddlewareExtensions
    {
        /// <summary>
        /// 使用全局异常处理中间件
        /// </summary>
        public static IApplicationBuilder UseGlobalExceptionHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<GlobalExceptionHandlerMiddleware>();
        }
    }
}

